package devTest.Ball 
{
	import devTest.CSpriteMovement;
	import devTest.IInitEndProtocol;
	import flash.display.Bitmap;
	import flash.display.Stage;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.media.Sound;
	
	/**
	 * ...
	 * @author miquel
	 */
	public class CBall extends CSpriteMovement implements IInitEndProtocol
	{	
		//stage
		private var myStage:			Stage = null;

		//Bitmap
		[Embed(source="../../../lib/images/ball.png")]
		private var m_classBall:		Class;
		
		//Sounds
		[Embed(source="../../../lib/sounds/paddle.mp3")]
		private var m_soPaddle:			Class;
		
		[Embed(source="../../../lib/sounds/wall.mp3")]
		private var m_soWall:			Class;
		
		[Embed(source="../../../lib/sounds/goal.mp3")]
		private var m_soGoal:			Class;
		
		private var m_soundGoal:		Sound = null;
		private var m_soundWall:		Sound = null;
		private var m_soundPaddle:		Sound = null;
		
		//dimensions of paddles to check collision
		private var m_iPadH:			uint	= 0;
		private var m_iPadW:			uint	= 0;
		private var m_iPadDivides:		uint 	= 0;
		
		//position y of paddles
		private var m_iP1x:				int		= 0;
		private var m_iP2x:				int		= 0;
		private var m_iP1y:				int		= 0;
		private var m_iP2y:				int		= 0;
		
		//ball values
		private var m_iMiddlePointY:	uint 	= 0;		//ball.height / 2;
		private var m_bBounced:			Boolean	= false;
				
		
		
		
		/**
		 * constructor
		 */
		public function CBall(s:Stage) 
		{
			super();
			myStage = s;
		}


		/**
		 * Init: Prepare object, read xml values and link the bitmap
		 * @param : the treenode xml with his information
		 * @return : if was created succesfully
		 */
		public function Init(_node:XML):Boolean
		{
			trace("object created: " + _node.@objclass);

			//read xml info 
			ReadXMLValues(_node);
			
			//create vector
			m_v2Direction   		= null;
			m_v2Direction			= new Point();
			InitVector();		
			
			//link bitmap with a sprite
			var l_bmpBall:Bitmap 	= null;
			l_bmpBall 				= new m_classBall();
			if (l_bmpBall != null)
			{
				trace("CBall: Init -> the bitmap is linked with the sprite: " + _node.@name );
				addChild(l_bmpBall);
			}
			else
			{
				trace("2:CBall: Init -> the bitmap is not linked with the sprite: " + _node.@name );
				return false;		
			}

			//used to test collisions
			m_iMiddlePointY = height / 2;
			
			//create sounds
			m_soundPaddle 			= new m_soPaddle();
			m_soundWall				= new m_soWall();
			m_soundGoal				= new m_soGoal();
			//play start
			m_soundGoal.play(0, 1);
			
			return true;	
		}
		
			
		/**
		 * Update: function for each frame
		 */
		public function Update():void 
		{
			Move();
		}
		
		
		/**
		 * Done: Procedure called to remove object (xml reload)
		 */
		public function Done():void
		{
			trace("3:CBall: Done -> removing Object");
		}
		

		/**
		 * GetPaddlesDimensionals: Get values from paddles
		 * @param : padH- height player 1
		 * @param : padW- width player 1
		 * @param : padDiv - divisions of paddle to apply a direction to ball
		 */
		public function GetPaddlesDimensionals( _padH:int, _padW:int, _padDiv:int):void
		{
			m_iPadH 		= _padH;
			m_iPadW 		= _padW;
			m_iPadDivides 	= _padDiv;
		}


		/**
		 * GetPaddlesPositionInit: Obtain init positions of two players
		 * @param : p1x- position player 1
		 * @param : p2x- position player 2
		 * @param : p1y- position player 1
		 * @param : p2y- position player 2
		 * @see : not is needed x coord.
		 */
		public function GetPaddlesPositionInit( _p1x:int, _p1y:int, _p2x:int, _p2y:int):void
		{
			m_iP1x = _p1x;
			m_iP2x = _p2x;
			m_iP1y = _p1y;
			m_iP2y = _p2y;
		}

		
		/**
		 * GetPaddlesPosition: Receives paddles position in each frame
		 * @param : p1y- position player 1
		 * @param : p2y- position player 2
		 * @see : not is needed x coord.
		 */
		public function GetPaddlesPosition( _p1y:int, _p2y:int):void
		{
			m_iP1y = _p1y;
			m_iP2y = _p2y;
		}
		

		/**
		 * Move: this method executes the movement of paddle
		 * @return : ever returns 0
		 */
		override public function Move():int
		{
			//move ball
			x += m_v2Direction.x * m_iSpeed;
			y += m_v2Direction.y * m_iSpeed;
			
			//check bounce with wall or paddles
			BounceWith();
			
			//check goals
			if (x + width > myStage.stageWidth)
			{
				m_soundGoal.play(0, 1);
				return 1;
			}
			if (x < 0)
			{
				m_soundGoal.play(0, 1);
				return -1;
			}
			return 0;
		}
		
		
		/**
		 * GetRandomDirection: this method gives a rand value to determine the ball direction
		 * @return : a value between 0-11
		 */
		public function GetRandomDirection():int
		{
			//get a value to know which angle apply. PLEASE see documentation about ball directions...
			var l_iAngle:int = Math.floor(Math.random() * 12);
			return l_iAngle;
		}
		
		
		/**
		 * GetRandomMidfieldPosition: this method gives a rand value to determine the Y position at beginning points
		 * @return : a value near the center of the screen
		 */
		public function GetRandomMidfieldPosition():int
		{
			//get a position since the pos 150-450 for a height stage of 600 
			var l_iAngle:int = Math.floor(Math.random() * myStage.stageHeight / 2) + myStage.stageHeight / 4;/*(max - min + 1)) + min;*/
			trace("get random midfield position x: "+ l_iAngle);
			return l_iAngle;
		}


		/**
		 * BallToMidfield: this procedure put the ball at point to replay, invisible to do a delay
		 */
		public function BallToMidfield():void
		{
			trace("ball to midfield");
			//ball to midfield after a goal
			x = myStage.stageWidth / 2;
			y = GetRandomMidfieldPosition();
			
			//vector director(0,0)
			InitVector();
			visible = false;
		}
		

		/**
		 * LauchToLeft: extracts a rand value to decide which angle the ball runs starting the point (will go to left)
		 */
		public function LauchToLeft():int
		{
			trace("will launch to left");
			//from midfield to left
			var l_iAngle:int = Math.floor(Math.random() * 5) + 7;	//5 possibles position, goes to pos 7, to left of screen
			trace("direction: " + l_iAngle);
			m_soundPaddle.play(0, 1);
			return l_iAngle;
		}
		

		/**
		 * LauchToRight: extracts a rand value to decide which angle the ball runs starting the point (will go to right)
		 */
		public function LauchToRight():int
		{
			trace("will launch to right");
			//from midfield to right
			var l_iAngle:int = Math.floor(Math.random() * 5);	//5 possibles position
			trace("direction: " + l_iAngle);
			m_soundPaddle.play(0, 1);
			return l_iAngle;
		}
		
		
		/**
		 * BounceWith: bound with walls and paddles
		 */
		public function BounceWith():void
		{
			//bounce negating Y component (with bottom and upper wall)
			if (y + height >= myStage.stageHeight)
			{
				y = myStage.stageHeight - height;
				m_v2Direction.y = -m_v2Direction.y;
				m_soundWall.play(0, 1);
				trace("ballY: " + y + ", m_v2DirectionY: " + m_v2Direction.y);
			}
			
			if (y <= 0)
			{
				y = 0;
				m_v2Direction.y = -m_v2Direction.y;
				m_soundWall.play(0, 1);
				trace("ballY: " + y + ", m_v2DirectionY: " + m_v2Direction.y);
			}
			
			//Allow bounce once
			if ( !m_bBounced )
			{
				if ( CollisionWithPaddle() )
				{
					m_soundPaddle.play(0, 1);
					trace("colliding!")
					//the ball take a new direction depending the height of paddle where it collides
					var i:int = GetNewPitch(); 
					trace("taking direction: " + i);
					
					//solves a little bug when the ball is hitted with the down side
					if (i == m_iPadDivides)
						i--;
					
					DecideDirectionNew( i );
					//when ball bounces, close the flag, to allow once
					m_bBounced = true;
				}
			}

			//it allow bounces again when crosses midfield
			if ((x > ((myStage.stageWidth / 2) - width / 2)) && (x < ((myStage.stageWidth / 2) + width / 2)))
				m_bBounced = false;
		}
		
		
		/**
		 * CollisionWithPaddle: check if ball collide with a paddle
		 * @see I don't use the collisions function of flash (HitTestObject) to improve the eficience. Just a square collision test.
		 */
		public function CollisionWithPaddle():Boolean 
		{
						//colliding with paddle 1
			if 	((((x <= (m_iP1x + m_iPadW)) && 										// si ball.x =< paddle1.x + paddle1.width
			   (x > m_iP1x) && 														// ball.x => paddle1.x
			   ((y + m_iMiddlePointY) < (m_iP1y + m_iPadH + m_iMiddlePointY)) && 	// (ball.y + ball.height/2) =< paddle1.y + paddle1.height + ball.height/2
			   ((y + m_iMiddlePointY) > (m_iP1y - m_iMiddlePointY))))			  	// (ball.y + ball.height/2) => paddle1.y - ball.height/2			
			 ||			//colliding with paddle 2
			   (((x < (m_iP2x + m_iPadW)) && 										// si ball.x =< paddle1.x + paddle1.width
			   ((x + width)>= m_iP2x) && 											// ball.x => paddle1.x
			   ((y + m_iMiddlePointY) < (m_iP2y + m_iPadH + m_iMiddlePointY)) && 	// (ball.y + ball.height/2) =< paddle1.y + paddle1.height + ball.height/2
			   ((y + m_iMiddlePointY) > (m_iP2y - m_iMiddlePointY)))))			  	// (ball.y + ball.height/2) => paddle1.y - ball.height/2			
			   
			{
				return true;
			}
			else
			{
				return false;	
			}
		}
		
		
		/**
		 * GetNewPitch: 
		 * @return : a new direction to move the ball depending the height of paddle where it collides
		 * @see : PLEASE see documentation about ball bounces
		 */
		public function GetNewPitch():int
		{	
			//Please see documentation about ball bounces
			var l_iPixelsSector:uint = (m_iPadH + height) / m_iPadDivides;
			var sector:			uint  = 0;
			
			//the ball goes to P1
			if (x < myStage.stageWidth / 2)  
			{
				sector = (y + m_iMiddlePointY) - ( m_iP1y - m_iMiddlePointY ); 
				sector /=  l_iPixelsSector;
				return sector;
			}
			
			//the ball goes to P2
			else
			{
				sector = (y + m_iMiddlePointY) - ( m_iP2y - m_iMiddlePointY );
				sector /=  l_iPixelsSector;
//				return sector + 7;
				return sector;
			}
		}
		

		/**
		 * DecideDirectionOld: to decide the ball's dir. vector before start the ball movement when it is stopped at
		 * 						start of each point.
		 * 						0-5 to right: (90º not allowed), 60º, 30º, 0º, 330º, 300º
		 * 						6-11 to left: (270º not allowed)240º, 210º, 180º, 150º, 120º, 
		 * 						@see : PLEASE, see documentation about ball movements and @see 'DecideDirectionNew'!
		 * @param : the value to decide the ball direction.
		 */
		public function DecideDirection(dir:int):void
		{
			switch(dir)
			{
				case 0:// 5PI/3	   300º
					m_v2Direction.x = 0.5;
					m_v2Direction.y = -0.886;
				break;	
				case 1://11PI/6	   330º
					m_v2Direction.x = 0.886;
					m_v2Direction.y = -0.5;				
				break;	
				case 2: //	2PI 		 0º
					m_v2Direction.x = 1;
					m_v2Direction.y = 0;
				break;
				case 3: //	PI/6 		30º
					m_v2Direction.x = 0.886;
					m_v2Direction.y = 0.5;
				break;
				case 4: //	PI/3		60º
					m_v2Direction.x = 0.5;
					m_v2Direction.y = 0.886;
				break;
				case 5: //	PI/2		90º
				//break;

				case 6: // 3PI/2	   270º
				//break;	
				case 7: // 4PI/3	   240º
					m_v2Direction.x = -0.5;
					m_v2Direction.y = -0.886;
				break;	
				case 8: // 7PI/6	   210º
					m_v2Direction.x = -0.886;
					m_v2Direction.y = -0.5;
				break;	
				case 9: //  PI		   180º	
					m_v2Direction.x = -1;
					m_v2Direction.y = 0;
				break;
				case 10: // 5PI/6	   150º
					m_v2Direction.x = -0.886;
					m_v2Direction.y = 0.5;
				break;
				case 11: // 2PI/3	   120º		
					m_v2Direction.x = -0.5;
					m_v2Direction.y = 0.886;
				break;
			}
		}

		
		/**
		 * DecideDirectionNew: to decide the ball's dir. vector before start the ball movement when it is stopped at
		 * 						start of each point. Used to other number of dividers.
		 * @param : the value to decide the ball direction.
		 */
		public function DecideDirectionNew(dir:int):void
		{
			var l_pMatrix:			Matrix 	= null;
			var l_pVectorDirector:	Point 	= null;

			var i:					int 	= 0;
						
			l_pMatrix			= new Matrix();
			l_pVectorDirector 	= new Point(0, -1.0); //to down
			
			if (l_pVectorDirector != null)
			{
				//rotates a portion to avoid vertical movement
				l_pMatrix.identity();			
				l_pMatrix.rotate(Math.PI/ (m_iPadDivides + 1) );
				l_pVectorDirector = l_pMatrix.transformPoint(l_pVectorDirector);
				
				while(i < dir)
				{
					//rotates the vector i times to have correct angle
					l_pMatrix.identity();			
					l_pMatrix.rotate( Math.PI/ (m_iPadDivides + 1) );
					l_pVectorDirector = l_pMatrix.transformPoint(l_pVectorDirector);
					i++;
				}
				
				m_v2Direction = l_pVectorDirector;
			
				//changes the direction whether is needed
				m_v2Direction.x *= (x > myStage.stage.stageWidth / 2)? -1 : 1;
			}
		}
	}

}